package com.ruoyi.system.service.impl;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.ObjectUtil;
import com.ruoyi.common.constant.CacheNames;
import com.ruoyi.common.core.domain.PageQuery;
import com.ruoyi.common.core.service.OssService;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.file.FileUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.oss.core.OssClient;
import com.ruoyi.oss.entity.UploadResult;
import com.ruoyi.oss.enumd.AccessPolicyType;
import com.ruoyi.oss.factory.OssFactory;
import com.ruoyi.system.domain.SysOss;
import com.ruoyi.system.domain.SysOssDraft;
import com.ruoyi.system.domain.SysOssTable;
import com.ruoyi.system.domain.dto.SysOssListInput;
import com.ruoyi.system.domain.dto.SysOssView;
import com.ruoyi.system.service.ISysOssService;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.babyfish.jimmer.Page;
import org.babyfish.jimmer.sql.JSqlClient;
import org.babyfish.jimmer.sql.ast.mutation.SaveMode;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * 文件上传 服务层实现
 *
 * @author Lion Li
 */

@RequiredArgsConstructor
@Service
public class SysOssServiceImpl implements ISysOssService, OssService {

    private final JSqlClient sqlClient;

    @Override
    public Page<SysOssView> queryPageList(SysOssListInput bo, PageQuery pageQuery) {
        SysOssTable table = SysOssTable.$;
        Page<SysOssView> sysOssViewPage = sqlClient.createQuery(table).where(
                        table.fileName().likeIf(bo.getFileName()),
                        table.originalName().likeIf(bo.getOriginalName()),
                        table.fileSuffix().eqIf(bo.getFileSuffix()),
                        table.url().likeIf(bo.getUrl()),
                        table.service().eqIf(bo.getService())
                ).select(table.fetch(SysOssView.class))
                .fetchPage(pageQuery.getPageNum(), pageQuery.getPageSize());
        List<SysOssView> list = sysOssViewPage.getRows().stream().map(this::matchingUrl).toList();
        return new Page<>(list, sysOssViewPage.getTotalRowCount(), sysOssViewPage.getTotalPageCount());
    }

    @Override
    public List<SysOssView> listByIds(Collection<String> ossIds) {
        List<SysOssView> list = new ArrayList<>();
        for (String id : ossIds) {
            SysOssView vo = SpringUtils.getAopProxy(this).getById(id);
            if (ObjectUtil.isNotNull(vo)) {
                list.add(this.matchingUrl(vo));
            }
        }
        return list;
    }

    @Cacheable(cacheNames = CacheNames.SYS_OSS, key = "#ossId", unless = "#result == null")
    @Override
    public SysOssView getById(String ossId) {
        return sqlClient.findById(SysOssView.class, ossId);
    }

    @Override
    public void download(String ossId, HttpServletResponse response) throws IOException {
        SysOssView sysOss = SpringUtils.getAopProxy(this).getById(ossId);
        if (ObjectUtil.isNull(sysOss)) {
            throw new ServiceException("文件数据不存在!");
        }
        FileUtils.setAttachmentResponseHeader(response, sysOss.getOriginalName());
        response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE + "; charset=UTF-8");
        OssClient storage = OssFactory.instance();
        try (InputStream inputStream = storage.getObjectContent(sysOss.getUrl())) {
            int available = inputStream.available();
            IoUtil.copy(inputStream, response.getOutputStream(), available);
            response.setContentLength(available);
        } catch (Exception e) {
            throw new ServiceException(e.getMessage());
        }
    }

    @Override
    public SysOssView upload(MultipartFile file) {
        String originalfileName = file.getOriginalFilename();
        String suffix = StringUtils.substring(originalfileName, originalfileName.lastIndexOf("."), originalfileName.length());
        OssClient storage = OssFactory.instance();
        UploadResult uploadResult;
        try {
            uploadResult = storage.uploadSuffix(file.getBytes(), suffix, file.getContentType());
        } catch (IOException e) {
            throw new ServiceException(e.getMessage());
        }
        // 保存文件信息
        SysOss sysOss = SysOssDraft.$.produce(oss -> {
            oss.setUrl(uploadResult.getUrl());
            oss.setFileSuffix(suffix);
            oss.setFileName(uploadResult.getFilename());
            oss.setOriginalName(originalfileName);
            oss.setService(storage.getConfigKey());
        });
        SysOssView sysOssVo = sqlClient.saveCommand(sysOss)
                .setMode(SaveMode.INSERT_ONLY)
                .execute(SysOssView.class)
                .getModifiedView();
        return this.matchingUrl(sysOssVo);
    }

    @Override
    public Boolean deleteWithValidByIds(Collection<String> ids, Boolean isValid) {
        if (isValid) {
            // 做一些业务上的校验,判断是否需要校验
        }
        SysOssTable table = SysOssTable.$;
        List<SysOss> list = sqlClient.findByIds(SysOss.class, ids);
        for (SysOss sysOss : list) {
            OssClient storage = OssFactory.instance(sysOss.service());
            storage.delete(sysOss.url());
        }
        return sqlClient.createDelete(table)
                .where(table.ossId().in(ids))
                .execute() > 0;
    }


    /**
     * 匹配Url
     *
     * @param oss OSS对象
     * @return oss 匹配Url的OSS对象
     */
    private SysOssView matchingUrl(SysOssView oss) {
        OssClient storage = OssFactory.instance(oss.getService());
        // 仅修改桶类型为 private 的URL，临时URL时长为120s
        if (AccessPolicyType.PRIVATE == storage.getAccessPolicy()) {
            oss.setUrl(storage.getPrivateUrl(oss.getFileName(), 120));
        }
        return oss;
    }

    @Override
    public String selectUrlByIds(String ossIds) {
        List<String> list = new ArrayList<>();
        String[] ossIdList = ossIds.split(",");
        for (String id : ossIdList) {
            SysOssView vo = SpringUtils.getAopProxy(this).getById(id);
            if (ObjectUtil.isNotNull(vo)) {
                list.add(this.matchingUrl(vo).getUrl());
            }
        }
        return String.join(",", list);
    }
}

